package com.cmes.system.core.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cmes.cache.service.CacheService;
import com.cmes.core.constant.enums.EnableStatus;
import com.cmes.core.utils.Preconditions;
import com.cmes.jdbc.service.GenericService;
import com.cmes.system.core.constant.ErrorCode;
import com.cmes.system.core.entity.RoleEntity;
import com.cmes.system.core.entity.RolePermissionEntity;
import com.cmes.system.core.entity.UserRoleEntity;
import com.cmes.system.core.repository.RoleRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 角色管理
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@Slf4j
@RequiredArgsConstructor
@Service
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public class RoleService extends GenericService<RoleRepository, RoleEntity> {

    private static final String CACHED_KEY = "system:role";
    private final CacheService cacheService;
    private final RolePermissionService rolePermissionService;
    private final UserRoleService userRoleService;

    @PostConstruct
    public void init() {
        cacheService.register(CACHED_KEY, "全部角色");
    }

    /**
     * 查询所有有效角色
     */
    public List<RoleEntity> listActiveRoles() {
        return cacheService.getCache(CACHED_KEY, () -> list(Wrappers.<RoleEntity>lambdaQuery()
                .select(RoleEntity::getId, RoleEntity::getCode)
                .eq(RoleEntity::getStatus, EnableStatus.ENABLE.getCode())));
    }

    // region 页面增删查改

    public void canAdd(RoleEntity entity) {
        checkCode(entity.getCode());
    }

    /**
     * 判断是否可修改
     *
     * @param id 角色ID
     */
    public void canUpdate(Long id) {
        checkActive(id);
    }

    /**
     * 检查角色是否可删除
     *
     * @param ids 角色ID
     */
    public void canDelete(List<Long> ids) {
        List<RoleEntity> dbs = checkExists(ids);
        // 系统角色不能删除
        checkSystem(dbs, "删除");
        // 有用户，不能删除，必须先将用户移出去
        checkHasMember(ids, "删除");
    }

    /**
     * 删除角色，同步删除角色与用户及权限的关联关系
     *
     * @param ids 角色ID
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteRole(List<Long> ids) {
        deleteBatch(ids);
        // 删除用户角色关系
        userRoleService.deleteByRoleIds(ids);
        // 删除角色权限关系
        rolePermissionService.deleteByRoleIds(ids);
    }

    /**
     * 判断是否可停用
     *
     * @param ids 角色ID
     */
    public void canDisable(List<Long> ids) {
        List<RoleEntity> dbs = checkExists(ids);
        checkStatus(dbs, EnableStatus.ENABLE, ErrorCode.ROLES_DISABLED);
        // 系统角色不能停用
        checkSystem(dbs, "停用");
        // 有用户，不能停用，必须先将用户移出去
        checkHasMember(ids, "停用");
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void disable(List<Long> ids) {
        update(Wrappers.<RoleEntity>lambdaUpdate()
                .set(RoleEntity::getStatus, EnableStatus.DISABLE.getCode())
                .in(RoleEntity::getId, ids)
                .eq(RoleEntity::getStatus, EnableStatus.ENABLE.getCode()));
    }

    /**
     * 判断是否可启用
     *
     * @param ids 角色ID
     */
    public void canEnable(List<Long> ids) {
        List<RoleEntity> dbs = checkExists(ids);
        checkStatus(dbs, EnableStatus.DISABLE, ErrorCode.ROLES_ENABLED);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void enable(List<Long> ids) {
        update(Wrappers.<RoleEntity>lambdaUpdate()
                .set(RoleEntity::getStatus, EnableStatus.ENABLE.getCode())
                .in(RoleEntity::getId, ids)
                .eq(RoleEntity::getStatus, EnableStatus.DISABLE.getCode()));
    }

    /**
     * 校验角色名称是否唯一
     *
     * @param code 角色编码
     */
    private void checkCode(String code) {
        Preconditions.check(() -> exists(Wrappers.<RoleEntity>lambdaQuery()
                .eq(RoleEntity::getCode, code)), ErrorCode.ROLE_CODE_EXISTS);
    }

    /**
     * 校验角色状态
     * - 角色是否存在
     * - 角色状态是否可用
     */
    private void checkActive(Long id) {
        RoleEntity db = checkExists(id);
        Preconditions.check(() -> EnableStatus.DISABLE.match(db.getStatus()), ErrorCode.ROLE_DISABLED);
    }

    /**
     * 校验角色是否存在
     *
     * @return 角色信息
     */
    private RoleEntity checkExists(Long id) {
        RoleEntity db = getById(id);
        Preconditions.isNull(db, ErrorCode.ROLE_NOT_EXISTS);
        return db;
    }

    /**
     * 校验角色是否存在
     *
     * @param ids 配置ID
     */
    private List<RoleEntity> checkExists(List<Long> ids) {
        List<RoleEntity> dbs = listByIds(ids);
        Preconditions.check(() -> dbs.size() != ids.size(), ErrorCode.ROLE_NOT_EXISTS);
        return dbs;
    }

    /**
     * 检查是否系统角色
     *
     * @param dbs     角色
     * @param operate 操作
     */
    private void checkSystem(List<RoleEntity> dbs, String operate) {
        List<String> codes = dbs.stream()
                .filter(RoleEntity::getSys)
                .map(RoleEntity::getCode)
                .collect(Collectors.toList());
        Preconditions.notEmpty(codes, ErrorCode.ROLE_IS_SYS, codes, operate);
    }

    /**
     * 检查角色下是否存在用户
     * @param roleIds     角色
     * @param operate 操作
     */
    private void checkHasMember(List<Long> roleIds, String operate) {
        Preconditions.check(() -> userRoleService.exists(Wrappers.<UserRoleEntity>lambdaQuery()
                .in(UserRoleEntity::getRoleId, roleIds)), ErrorCode.ROLE_HAS_USER, operate);
    }

    /**
     * 检查角色是否停用
     *
     * @param dbs 角色
     */
    private void checkStatus(List<RoleEntity> dbs, EnableStatus expectStatus, ErrorCode errorCode) {
        List<String> codes = dbs.stream()
                .filter(v -> !expectStatus.match(v.getStatus()))
                .map(RoleEntity::getCode)
                .collect(Collectors.toList());
        Preconditions.notEmpty(codes, errorCode, codes);
    }

    // endregion

    // region 登录鉴权

    public Set<Long> authPermissionIds(Set<Long> roleIds) {
        return rolePermissionService.listDistinctObjs(Wrappers.<RolePermissionEntity>lambdaQuery()
                .select(RolePermissionEntity::getPermissionId)
                .in(RolePermissionEntity::getRoleId, roleIds), RolePermissionEntity::getPermissionId);
    }

    // endregion

    // region 缓存操作

    public void removeCache() {
        try {
            cacheService.removeCache(CACHED_KEY);
        } catch (Exception e) {
            log.error("删除缓存失败[role]", e);
        }
    }

    // endregion

}
